// -*-mode:c++; coding:utf-8-*-
#ifndef _XBASE_DYNAMIC_BUFFER_HPP_
#define _XBASE_DYNAMIC_BUFFER_HPP_

#include <algorithm>
#include <limits>
#include <memory>
#include <stdexcept>
#include <streambuf>
#include <vector>
#include <cstring>

#include <boost/asio/buffer.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/noncopyable.hpp>

#include "config.hpp"
#include "intrusive_ptr_helper.hpp"

namespace xbase
{

	struct increase_traits
	{
		enum
		{
			delta = 1024 // 初始分配或每次增加的最小值
		};
	};

	template<typename Allocator,
		 typename IncreaseTraits = increase_traits>
	class basic_simple_buffer
		: private boost::noncopyable
	{
	public:
		typedef IncreaseTraits increase_traits;
		typedef typename Allocator::template rebind<char>::other allocator_type;

		typedef boost::asio::const_buffers_1 const_buffers_type;
		typedef boost::asio::mutable_buffers_1 mutable_buffers_type;

	public:
		// 默认构造函数中不分配空间，在第一次prepare的时候才分配，避免无用的分配
		explicit
		basic_simple_buffer(std::size_t size = 0,
				    allocator_type allocator = allocator_type())
			: m_allocator(allocator)
			, m_storage(0)
			, m_size(size)
			, m_data_begin(0)
			, m_data_end(0) {
			if(m_size > 0)
			{
				m_storage = allocator.allocate((std::max<std::size_t>)(m_size,
										       increase_traits::delta));
				m_data_end = m_data_begin = m_storage;
			}
		}

		~basic_simple_buffer() {
			release();
		}

		std::size_t max_size() const {
			return m_size;
		}

		std::size_t size() const {
			return m_data_end - m_data_begin;
		}

		// 总的可用空间大小，即不重分配内存即可得到的最大内存大小
		std::size_t total_space() const {
			return m_size - size();
		}

		// 剩余可用空间大小，即不整理缓存即可得到的最大内存大小
		std::size_t left_space() const {
			return m_size - (m_data_end - m_storage);
		}

		void clear() {
			m_data_begin = m_data_end = m_storage;
		}

		mutable_buffers_type prepare(const std::size_t n) {
			if(m_storage == 0)
			{
				const std::size_t new_size = (std::max<std::size_t>)(n,
										    increase_traits::delta);
				m_storage = m_allocator.allocate(new_size);
				m_size = new_size;
				m_data_begin = m_data_end = m_storage;
			}
			else if(m_data_end + n > m_storage + m_size)
			{
				const std::size_t data_size = size();
				const std::size_t total_space = m_size - data_size;
				if(n > total_space)
				{
					const std::size_t new_size = data_size
						+ (std::max<std::size_t>)(n, increase_traits::delta);
					char * const new_storage = m_allocator.allocate(new_size);
					std::memcpy(new_storage,
						    m_data_begin,
						    data_size);
					m_allocator.deallocate(m_storage, m_size);
					m_storage = new_storage;
					m_size = new_size;
				}
				else
				{
					std::memmove(m_storage,
						     m_data_begin,
						     data_size);
				}
				m_data_begin = m_storage;
				m_data_end = m_data_begin + data_size;
			}
			// else it's enough
			return boost::asio::buffer(boost::asio::mutable_buffer(m_data_end,
									       n));
		}

		const_buffers_type data() const {
			return boost::asio::buffer(boost::asio::const_buffer(m_data_begin,
									     size()));
		}
	
		void commit(std::size_t n) {
			m_data_end += n;
			assert(m_data_end <= m_storage + m_size);
		}
	
		void consume(std::size_t n) {
			m_data_begin += n;
			assert(m_data_begin <= m_data_end);
		}

		template<typename Traits>
		void swap(basic_simple_buffer<allocator_type, Traits> &other) {
			// allocator is the same
			std::swap(m_storage, other.m_storage);
			std::swap(m_size, other.m_size);
			std::swap(m_data_begin, other.m_data_begin);
			std::swap(m_data_end, other.m_data_end);
		}

		void release() {
			if(m_storage != 0)
			{
				m_allocator.deallocate(m_storage, m_size);
				m_data_begin = m_data_end = m_storage = 0;
				m_size = 0;
			}
		}

	private:
		allocator_type m_allocator;
	
		char *m_storage;
		std::size_t m_size;

		char *m_data_begin;
		char *m_data_end;
	};

	typedef basic_simple_buffer<std::allocator<char> > simple_buffer;

	template<typename Allocator>
	struct basic_ref_buffer
	{
		typedef basic_ref_buffer<Allocator> this_type;
		typedef std::size_t size_type;
	public:
		typedef boost::intrusive_ptr<this_type> pointer;
		typedef Allocator allocator_type;

		basic_ref_buffer(std::size_t size) // set size
			: m_size(size) {}

		~basic_ref_buffer() {} // do nothing

		char *get() {
			// this + 1，跳过当前对象大小
			return reinterpret_cast<char*>(this + 1);
		}

		const char *get() const {
			return reinterpret_cast<const char*>(this + 1);
		}

		size_type size() const {
			return m_size;
		}

		boost::asio::mutable_buffer buffer() {
			return boost::asio::mutable_buffer(get(), size());
		}

		boost::asio::const_buffer data() const {
			return boost::asio::const_buffer(get(), size());
		}

		static inline
		pointer allocate(size_type size) {
			allocator_type allocator;
			const size_type adj_size = size + sizeof(this_type);
			char *p = allocator.allocate(adj_size);
			this_type *ptr = reinterpret_cast<this_type*>(p);
			new (ptr) this_type(size); // construct
			return pointer(ptr);
		}

	private:
		// 注意字节对齐问题
		reference_counter m_ref_counter;
		size_type m_size;

		friend inline
		void intrusive_ptr_add_ref(basic_ref_buffer<Allocator> *p) {
			p->m_ref_counter.inc();
		}

		friend inline
		void intrusive_ptr_release(basic_ref_buffer<Allocator> *p) {
			if(p->m_ref_counter.dec_and_test())
			{
				Allocator allocator;
				p->~basic_ref_buffer<Allocator>();
				allocator.deallocate(reinterpret_cast<char*>(p),
						     p->size() + sizeof(basic_ref_buffer<Allocator>));
			}
		}
	};

	typedef basic_ref_buffer<default_char_allocator> ref_buffer;

} // namespace xbase

namespace std
{
	// 重载标准算法中的swap
	template<typename Alloc,
		 typename Traits1, typename Traits2>
	void swap(xbase::basic_simple_buffer<Alloc, Traits1> &rhs,
		  xbase::basic_simple_buffer<Alloc, Traits2> &lhs) {
		rhs.swap(lhs);
	}
}


#endif	// _XBASE_DYNAMIC_BUFFER_HPP_
